package com.gitee.wsl.compose.ui.richtext.base


import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.style.TextOverflow
import com.gitee.wsl.compose.ext.text.getConsumableAnnotations
import com.gitee.wsl.compose.ui.richtext.handler.LinkClickHandler
import com.gitee.wsl.compose.ui.richtext.handler.LocalLinkClickHandler
import com.gitee.wsl.compose.ui.richtext.RichTextScope
import com.gitee.wsl.compose.ui.richtext.content.RichTextString
import com.gitee.wsl.compose.ui.richtext.content.inline.manageInlineTextContents
import com.gitee.wsl.compose.ui.richtext.theme.RichTextStringStyle
import com.gitee.wsl.compose.ui.richtext.annotatedTextParagraphContents
import com.gitee.wsl.compose.ui.richtext.theme.currentRichTextStyle

/**
 * Renders a [RichTextString] as created with [richTextString].
 *
 * @sample com.halilibo.richtext.ui.previews.TextPreview
 */
@Suppress("UnusedBoxWithConstraintsScope")
@Composable
fun RichTextScope.Text(
  text: RichTextString,
  modifier: Modifier = Modifier,
  onTextLayout: (TextLayoutResult) -> Unit = {},
  softWrap: Boolean = true,
  overflow: TextOverflow = TextOverflow.Clip,
  maxLines: Int = Int.MAX_VALUE
) {
  val style = currentRichTextStyle.stringStyle
  val contentColor = currentContentColor
  val annotated = remember(text, style, contentColor) {
    val resolvedStyle = (style ?: RichTextStringStyle.Default).resolveDefaults()
    text.toAnnotatedString(resolvedStyle, contentColor)
  }

  var textModifier = modifier

  val layoutResult = remember {
    mutableStateOf<TextLayoutResult?>(null)
  }

  if (text.paragraphContents.isNotEmpty()) {
    textModifier = textModifier.annotatedTextParagraphContents(
      text,
      layoutResult
    )
  }

  val inlineContents = remember(text) { text.getInlineContents() }

  if (inlineContents.isEmpty()) {
    // cheap path
    val linkClickHandler = LocalLinkClickHandler.current ?: LocalUriHandler.current
    ClickableText(
      text = annotated,
      modifier = textModifier,
      onTextLayout = onTextLayout,
      softWrap = softWrap,
      overflow = overflow,
      maxLines = maxLines,
      isOffsetClickable = { offset ->
        annotated.getConsumableAnnotations(text.formatObjects, offset).any()
      },
      onClick = { offset ->
        annotated.getConsumableAnnotations(text.formatObjects, offset)
          .firstOrNull()
          ?.let { link ->
            when (linkClickHandler) {
              is LinkClickHandler -> linkClickHandler.onClick(link.destination)
              is UriHandler -> linkClickHandler.openUri(link.destination)
            }
          }
      }
    )
  } else {
    // expensive constraints reading path
    BoxWithConstraints(modifier = textModifier) {
      val inlineTextContents = manageInlineTextContents(
        inlineContents = inlineContents,
        textConstraints = constraints
      )

      val linkClickHandler = LocalLinkClickHandler.current ?: LocalUriHandler.current

      ClickableText(
        text = annotated,
        onTextLayout =  {
          layoutResult.value = it
          onTextLayout(it)
        },
        inlineContent = inlineTextContents,
        softWrap = softWrap,
        overflow = overflow,
        maxLines = maxLines,
        isOffsetClickable = { offset ->
          annotated.getConsumableAnnotations(text.formatObjects, offset).any()
        },
        onClick = { offset ->
          annotated.getConsumableAnnotations(text.formatObjects, offset)
            .firstOrNull()
            ?.let { link ->
              when (linkClickHandler) {
                is LinkClickHandler -> linkClickHandler.onClick(link.destination)
                is UriHandler -> linkClickHandler.openUri(link.destination)
              }
            }
        }
      )
    }
  }
}

